package server.chat;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.concurrent.GlobalEventExecutor;

import java.net.SocketAddress;

public class ChatServerHandler extends SimpleChannelInboundHandler<String> {
    /**
     * 定义 channelGroup
     */
    private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    /**
     * 当继承   SimpleChannelInboundHandler<T>  类  需要实现  channelRead0 方法  用来接受对应的数据并发送
     * 其中的泛型T 则表示  会接收到的数据类型,  如果确定传输的是字符串类型的  ,那就只需要套定义String 即可
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        System.out.println("客户端发来消息:"+msg);
        Channel channel = ctx.channel();
        //从 channelGroup中排除自己
        for (Channel ch : channelGroup) {
            if(ch == channel){
                //说明是自己,
                ch.writeAndFlush("[自己]: "+msg+"\n");
            }else{
                ch.writeAndFlush("["+channel.remoteAddress()+"]: "+msg+"\n");
            }
        }
    }

    /**
     *  客户端与服务器端建立链接的时候调用
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        //当有客户端链接的时候 会调用此方法, 那么就在这里将  channel 添加到channelGroup中,
        Channel channel = ctx.channel();
        //广播给所有客户端消息,writeAndFlush 会将消息发送给所有的channel
        channelGroup.writeAndFlush("[服务器广播] 用户"+channel.remoteAddress()+ "进去群聊\n");
        channelGroup.add(channel);
    }



    /**
     * 客户端与服务端失去连接调用
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        //断开链接的时候 调用此方法,  需要将channel从  channelGrouop 中移除
        Channel channel = ctx.channel();
        SocketAddress socketAddress = channel.remoteAddress();
        channelGroup.remove(channel);
        //通知其他所有人  改channel已断开链接
        channelGroup.writeAndFlush("[服务器广播] 用户"+socketAddress+ "离开群聊\n");
    }

    /**
     *  注册器,注册时调用  channel注册到一个EventLoop。
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelRegistered");
        super.channelRegistered(ctx);
    }

    /**
     *  channel已经创建，但是未注册到一个EventLoop里面，也就是没有和Selector绑定
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelUnregistered");
        super.channelUnregistered(ctx);
    }


    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("[服务器] 用户 "+ctx.channel().remoteAddress()+"上线");
        super.channelActive(ctx);
    }

    /**
     * 频道无效时调用  channel处于非活跃状态，没有连接到远程主机
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("[服务器] 用户 "+ctx.channel().remoteAddress()+"下线");
        super.channelInactive(ctx);
    }



//    @Override
//    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//        System.out.println("channelReadComplete");
//        ctx.flush();
//    }



    /**
     * 当发生异常时调用
     *
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("[异常]:"+cause.getMessage());
        //当发生异常时  会访问此方法, 在此处  可以对异常进行处理, 也可以通过 ChannelHandlerContext 进行 如  close(关闭) 等操作
        ctx.close();
    }
}
